昨日我們講解了JdbcTemplate剩下CRUD的部分,也說明了它跟Java Bean之間如何應用。
今日我們將進入重頭戲,declarative transaction management,在這之前我們先簡介一下何謂Transaction與Transaction managemet的兩種方式,最後以一個例子說明沒有事務管理會發生的情況。
Transaction是指對資料庫進行一連串的操作,必須全部成功否則全部回到未執行的狀態。以銀行轉帳的例子來說,A帳戶轉入100元到B帳戶,必須執行兩個SQL語法,分別是A帳戶扣款100元與B帳戶轉入100元,兩個指令必須同時成立,否則皆未執行。
整個交易視為一個執行單元,具有不可分割性,要就全部成功,不然就全部取消。
交易完成後,寫入的資料須符合所有既定的規則,如constraints,cascades,triggers等
Transaction在執行時往往是併發(concurrently)執行,可能多個transaction在操作相同的資料,因此transaction需要適度隔離,以免彼此互相影響。
交易完成即代表數據修改永久保存下來,不因系統斷線、當機而受影響。
交易管理可以分為兩種方式,如下
過去經典轉帳
Connection con = null;
String sql1 = "UPDATE ACCOUNT SET BALANCE = BALANCE -100 WHERE id = 1";
String sql2 = "UPDATE ACCOUNT SET BALANCE = BALANCE +100 WHERE id = 2";
PrepareStatement ps = null
try{
//取得資料庫連線
con = JDBCUtils.getConnection();
//設定非自動提交
con.setAutoCommit(false);
ps = con.prepareStatement(sql1);
ps.executeUpdate();
ps = con.prepareStatement(sql2);
ps.executeUpdate();
//提交
con.commit()
}catch(Exception e){
con.rollback();
e.printStackTrace();
}finally{
//關閉連線資源
JDBCUtils.close();
}
現在可以透過Spring TransactionTemplate來簡化操作
由於事務管理的程非常固定,可以作為一種Cross-cutting concerns,可透過AOP的方式來達成。Spring已替我們完成transaction的aspect,我們只需透過幾個設定就能使用,之後也將著重於聲明式事務作介紹。
UserService{
@Transaction
public void doTransfer(){
...
}
}
建立Account資料表
CREATE TABLE ACCOUNT(
ACCOUNT_ID NVARCHAR(10) NOT NULL,
BALANCE INT
)
INSERT INTO ACCOUNT VALUES ('1',1000);
INSERT INTO ACCOUNT VALUES ('2',1000);
建立Account Java Bean
public class Account {
private String accountId;
private Integer balance;
//getter setter toString 略
}
創建Dao
@Repository
public class AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void addMoney(String accountId,Integer amount){
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE +? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql,amount,accountId);
}
public void minusMoney(String accountId,Integer amount){
String sql = "UPDATE ACCOUNT SET BALANCE = BALANCE -? WHERE ACCOUNT_ID = ?";
jdbcTemplate.update(sql,amount,accountId);
}
}
創建Transaction會有異常狀況的Service
@Service
public class AccountService {
@Autowired
private AccountDao accountDao;
public void transferMoney(String from, String to , Integer amount){
accountDao.addMoney(to,amount);
int a =1/0;
accountDao.minusMoney(from,amount);
System.out.println("轉帳完成:"+from+" to "+to+" 金額:"+amount);
}
}
Result
僅有帳戶2被加到錢,帳戶1未扣錢